#include "ASLabel.h"
#include "..\base\ASStdLib.h"
#include "..\base\ASGraphic.h"

#define SHOW_LINE_GAP 3
#define MAX_LINE 2000

static ASWCHAR print_buf[64];
static as_ret as_ctrl_label_calc_offset(as_label* label_p);
static ASINT as_ctrl_lable_get_total_height_internal(as_control* ctrl_p);
static void as_ctrl_label_analyze_text(as_label* label_p);

static ASINT as_label_get_wstr_width(as_label* l_p)
{
    if(l_p->if_small)
        return as_get_wstr_width_sf(l_p->text);
    else
        return as_get_wstr_width(l_p->text);
}

static ASINT as_label_get_wstr_height(as_label* l_p)
{
    if(l_p->if_small)
        return as_get_wstr_height_sf(l_p->text);
    else
        return as_get_wstr_height(l_p->text);
}

static as_ret as_ctrl_label_change_text(as_control* ctrl_p, ASWSTR text)
{
    as_label* label_p = (as_label*)ctrl_p;
    ASINT len;

    if(!label_p || !text)
    {
        return AS_INVALID_PARAM;
    }

    len = as_wstrlen(text);

    if(len <= 0)
        return AS_INVALID_PARAM;

    if(label_p->text)
    {
        as_free(label_p->text);
    }

    label_p->text = as_calloc((len + 1) * sizeof(ASWCHAR));

    if(!label_p->text)
        return AS_MEMORY_NOT_ENOUGH;

    as_wstrcpy(label_p->text, text);

    if(as_label_get_wstr_width(label_p) > ctrl_p->area.w)
    {
        label_p->if_multiline = TRUE;
    }
    else
    {
        label_p->if_multiline = FALSE;
    }

    as_ctrl_label_calc_offset(label_p);
    label_p->total_height = as_ctrl_lable_get_total_height_internal((as_control*)label_p);
#ifndef MAX_LINE
    as_ctrl_label_analyze_text(label_p);
#endif
    return AS_OK;
}

static as_ret as_ctrl_label_set_text_color(as_control* ctrl_p, ASUINT color)
{
    as_ret ret = AS_OK;
    as_label* label_p = (as_label*)ctrl_p;

    if(!label_p)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    label_p->text_color = color;
    return ret;
}

static as_ret as_ctrl_label_get_text_color(as_control* ctrl_p, ASUINT* color_p)
{
    as_ret ret = AS_OK;
    as_label* label_p = (as_label*)ctrl_p;

    if(!label_p || !color_p)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    *color_p = label_p->text_color;
    return ret;
}

static as_ret as_ctrl_label_set_text_offset(as_control* ctrl_p, ASINT16 x_off, ASINT16 y_off)
{
    as_ret ret = AS_OK;
    as_label* label_p = (as_label*)ctrl_p;

    if(!label_p)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    label_p->text_x_off = x_off;
    label_p->text_y_off = y_off;
    return ret;
}

static as_ret as_ctrl_label_set_bg_hidden(as_control* ctrl_p, ASINT if_hidden)
{
    as_label* label_p = (as_label*)ctrl_p;

    if(!label_p || label_p->if_bg_hidden == if_hidden)
        return AS_INVALID_PARAM;

    label_p->if_bg_hidden = if_hidden;
    return AS_OK;
}

static as_ret as_ctrl_label_set_bg_color(as_control* ctrl_p, ASUINT color)
{
    as_ret ret = AS_OK;
    as_label* label_p = (as_label*)ctrl_p;

    if(!label_p)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    label_p->bg_color = color;
    return ret;
}

static as_ret as_ctrl_label_set_bg_frame_color(as_control* ctrl_p, ASUINT color)
{
    as_ret ret = AS_OK;
    as_label* label_p = (as_label*)ctrl_p;

    if(!label_p)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    label_p->bg_frame_color = color;
    return ret;
}

static as_ret as_ctrl_label_set_bg_img(as_control* ctrl_p, as_image_src_type type, ASWSTR path)
{
    as_label* label_p = (as_label*)ctrl_p;

    if(!label_p || !path)
        return AS_INVALID_PARAM;

    if(label_p->bg_img)
    {
        as_ctrl_image_destroy(label_p->bg_img);
        label_p->bg_img = ASNULL;
    }

    label_p->bg_img = as_ctrl_image_create(ctrl_p->area, type, path, FALSE);

    if(label_p->bg_img)
    {
        label_p->bg_img->set_show_type((as_control*)label_p->bg_img, AS_IMAGE_TILED);
        return AS_OK;
    }

    return AS_MEMORY_NOT_ENOUGH;
}

static as_ret as_ctrl_label_calc_offset(as_label* label_p)
{
    ASINT str_w = 0, str_h = 0;
    as_ret ret = AS_OK;
    as_label_text_layout layout;

    if(!label_p)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    if(label_p->if_multiline)
        return AS_OK;

    str_w = as_label_get_wstr_width(label_p);
    str_h = as_label_get_wstr_height(label_p);
    layout = label_p->layout;

    /*horizontal*/
    if(layout & AS_LABEL_TEXT_LEFT)
    {
        label_p->text_x_off = 0;
    }
    else if(layout & AS_LABEL_TEXT_H_CENTER)
    {
        label_p->text_x_off = ((label_p->base.area.w - str_w) >> 1);
    }
    else if(layout & AS_LABEL_TEXT_RIGHT)
    {
        label_p->text_x_off = label_p->base.area.w - str_w;
    }

    /*vertical*/
    if(layout & AS_LABEL_TEXT_TOP)
    {
        label_p->text_y_off = 0;
    }
    else if(layout & AS_LABEL_TEXT_V_CENTER)
    {
        label_p->text_y_off = ((label_p->base.area.h - str_h) >> 1);
    }
    else if(layout & AS_LABEL_TEXT_BOTTOM)
    {
        label_p->text_y_off = label_p->base.area.h - str_h;
    }

    return ret;
}

static as_ret as_ctrl_label_set_layout(as_control* ctrl_p, as_label_text_layout layout)
{
    as_ret ret = AS_OK;
    as_label* label_p = (as_label*)ctrl_p;

    if(!label_p)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    label_p->layout = layout;
    return as_ctrl_label_calc_offset(label_p);
}

static as_ret as_ctrl_label_set_if_small(as_control* ctrl_p, ASBOOL if_small)
{
    as_label* label_p = (as_label*)ctrl_p;

    if(!label_p || label_p->if_small == if_small)
        return AS_INVALID_PARAM;

    label_p->if_small = if_small;

    if(as_label_get_wstr_width(label_p) > ctrl_p->area.w)
    {
        label_p->if_multiline = TRUE;
    }
    else
    {
        label_p->if_multiline = FALSE;
    }

    as_ctrl_label_calc_offset(label_p);
    label_p->total_height = as_ctrl_lable_get_total_height_internal((as_control*)label_p);
    return AS_OK;
}

static ASINT as_ctrl_label_get_str_oneline(ASWCHAR* out_buf, ASINT out_buf_len, ASINT limit_w, ASWCHAR* start_p, ASBOOL if_small)
{
    ASINT str_len, tmp_display_len;
    ASINT i = 0, buf_max;

    if(!out_buf || !start_p)
        return 0;

    str_len = as_wstrlen(start_p);

    if(str_len <= 0)
        return 0;

    memset(out_buf, 0, out_buf_len);
    buf_max = out_buf_len / sizeof(ASWCHAR);

    for(i = 0; i < str_len, i < buf_max; i++)
    {
        *(out_buf + i) = *(start_p + i);

        if(if_small)
            tmp_display_len = as_get_wstr_width_sf(out_buf);
        else
            tmp_display_len = as_get_wstr_width(out_buf);

        if(*(start_p + i) == 0x0d00/*L'\r'*/ && *(start_p + i + 1) == 0x0a00/*L'\n'*/)
        {
            if(i == 0)
                return -2;
            else
                return i + 1;
        }

        if(*(start_p + i) == L'\n')
        {
            if(i == 0)
                return -1;
            else
                return i;
        }

        if(tmp_display_len >= limit_w)
            break;
    }

    *(out_buf + i) = ASNULL;
    i--;
    return i;
}

static ASINT as_ctrl_lable_get_total_height(as_control* ctrl_p)
{
    as_label* label_p = (as_label*)ctrl_p;

    if(!label_p)
        return 0;

    return label_p->total_height;
}

static ASINT as_ctrl_lable_get_total_height_internal(as_control* ctrl_p)
{
    as_label* label_p = (as_label*)ctrl_p;
    ASINT height = 0;
    ASINT str_h, str_w, str_off = 0, str_cnts;
    ASWCHAR* start_p, *end_p;
#ifdef MAX_LINE
    ASINT i = 0;
    as_text_pos_info* info_p;
#endif

    if(!label_p || !label_p->text)
        return 0;

    str_cnts = as_wstrlen(label_p->text);

    if(str_cnts <= 0)
        return 0;

    if(!label_p->if_multiline)
        return ctrl_p->area.h;

#ifdef MAX_LINE
    as_log_ctrl("<ctrl>----as_ctrl_lable_get_total_height_internal");

    if(!label_p->text_pos_info)
    {
        label_p->text_pos_info = as_calloc(MAX_LINE * sizeof(as_text_pos_info));

        if(!label_p->text_pos_info)
            return 0;
    }

#endif
    str_w = as_label_get_wstr_width(label_p);
    memset(print_buf, 0, sizeof(print_buf));
    str_h = as_label_get_wstr_height(label_p);
    label_p->single_line_height = str_h + SHOW_LINE_GAP;
    start_p = label_p->text;
    end_p = start_p + str_cnts;

    while(start_p <= end_p)
    {
        str_off = as_ctrl_label_get_str_oneline(print_buf, sizeof(print_buf), ctrl_p->area.w, start_p, label_p->if_small);

        if(str_off == 0)
            break;

#ifdef MAX_LINE
        info_p = label_p->text_pos_info + i;
        info_p->start_p = start_p;
#endif

        if(str_off < 0)
        {
            height += label_p->single_line_height;
            start_p += (-str_off);
#ifdef MAX_LINE
            info_p->cnts = (-str_off);
            i++;
#endif
            continue;
        }

        height += label_p->single_line_height;
        start_p += (str_off + 1);
#ifdef MAX_LINE
        info_p->cnts = (str_off + 1);
        i++;
#endif
    }

    as_log_ctrl("<ctrl>----as_ctrl_lable_get_total_height_internal     over");
    return height;
}

//in the life cycle of label,we just need to analyze its text once.
#ifndef MAX_LINE
static void as_ctrl_label_analyze_text(as_label* label_p)
{
    ASINT lines, i = 0;
    ASINT str_off = 0, str_cnts;
    ASWCHAR* start_p, *end_p;
    as_text_pos_info* info_p = ASNULL;

    if(!label_p || !label_p->text || !label_p->if_multiline)
        return;

    lines = label_p->total_height / label_p->single_line_height;
    label_p->text_pos_info = as_calloc(sizeof(as_text_pos_info) * lines);

    if(!label_p->text_pos_info)
        return;

    memset(print_buf, 0, sizeof(print_buf));
    start_p = label_p->text;
    str_cnts = as_wstrlen(label_p->text);
    end_p = start_p + str_cnts;

    while(start_p <= end_p)
    {
        str_off = as_ctrl_label_get_str_oneline(print_buf, sizeof(print_buf), label_p->base.area.w, start_p, label_p->if_small);

        if(str_off == 0)
            break;

        info_p = label_p->text_pos_info + i;
        info_p->start_p = start_p;

        if(str_off < 0)
        {
            start_p += (-str_off);
            info_p->cnts = (-str_off);
            i++;
            continue;
        }

        start_p += (str_off + 1);
        info_p->cnts = (str_off + 1);
        i++;
    }
}
#endif//MAX_LINE

static void as_ctrl_lable_paint_multiline(as_label* label_p, ASUINT text_color)
{
    ASINT i = 0, total;
    as_text_pos_info* pos_info_p;
    ASINT x, y, w;
    as_rect clip;

    if(!label_p || !label_p->text || !label_p->text_pos_info)
        return;

    total = label_p->total_height / label_p->single_line_height;
    x = label_p->base.area.x + label_p->text_x_off;
    w = label_p->base.area.x + label_p->base.area.w - x;

    for(i = 0; i < total; i++)
    {
        y = label_p->text_y_off + label_p->base.area.y + label_p->single_line_height * i;

        if(y < label_p->base.area.y)
            continue;

        if(y > label_p->base.area.y + label_p->base.area.h)
            break;

        {
            pos_info_p = label_p->text_pos_info + i;

            if(!pos_info_p->start_p)
                break;

            clip.x = x;
            clip.y = y;
            clip.w = w;
            clip.h = AS_MIN(label_p->single_line_height, label_p->base.area.y + label_p->base.area.h - clip.y);
            as_memset(print_buf, 0, sizeof(print_buf));
            as_wstrncpy(print_buf, pos_info_p->start_p, pos_info_p->cnts);

            if(label_p->if_small)
            {
                as_draw_text_clip_sf(print_buf, x, y, &clip, text_color);
            }
            else
            {
                as_draw_text_clip(print_buf, x, y, &clip, text_color);
            }
        }
    }
}

static void as_ctrl_label_paint(as_control* ctrl_p)
{
    as_label* label_p = (as_label*)ctrl_p;
    as_rect* rect_p = ASNULL;
    ASINT layer;
    ASUINT8* paint_buf;

    if(!label_p || ctrl_p->if_hidden)
        return;

    layer = label_p->base.show_hdlr;
    rect_p = &label_p->base.area;
    paint_buf = as_get_layer_buffer_by_hdlr(label_p->base.show_hdlr);

    if(layer < 0 || as_set_layer_clip_ex(layer, rect_p) == AS_IGNORE)
        return;

    //fill the area of label with background color
    if(!label_p->if_bg_hidden)
    {
        if(label_p->bg_img)
        {
            label_p->bg_img->base.paint_hdlr((as_control*)label_p->bg_img);
        }
        else
        {
            as_fill_rect(rect_p, label_p->bg_frame_color, label_p->bg_color);
        }
    }

    if(label_p->text && as_wstrlen(label_p->text) > 0)
    {
        if(label_p->if_multiline)
        {
            as_ctrl_lable_paint_multiline(label_p, label_p->text_color);
        }
        else
        {
            as_rect gclip;
            as_get_global_clip(&gclip);

            if(label_p->if_small)
            {
                as_draw_text_clip_sf(label_p->text,
                                     rect_p->x + label_p->text_x_off, rect_p->y + label_p->text_y_off,
                                     &gclip, label_p->text_color);
            }
            else
            {
                as_draw_text_clip(label_p->text,
                                  rect_p->x + label_p->text_x_off, rect_p->y + label_p->text_y_off,
                                  &gclip, label_p->text_color);
            }
        }
    }

    as_reset_layer_clip(layer);
}

as_label* as_ctrl_label_create(as_rect area, ASWSTR text)
{
    as_label* label_p = ASNULL;
    ASINT len;

    if(!text)
    {
        return label_p;
    }

    label_p = as_calloc(sizeof(as_label));

    if(!label_p)
    {
        return label_p;
    }

    len = as_wstrlen(text);

    if(len > 0)
    {
        label_p->text = as_calloc((len + 1) * sizeof(ASWCHAR));

        if(!label_p->text)
        {
            as_free(label_p);
            return ASNULL;
        }

        as_wstrcpy(label_p->text, text);
    }

    as_ctrl_init((as_control*)label_p, area);
    label_p->base.paint_hdlr = as_ctrl_label_paint;
    label_p->base.ctrl_type = AS_CTRL_LABEL;
    label_p->change_text = as_ctrl_label_change_text;
    label_p->set_bg_color = as_ctrl_label_set_bg_color;
    label_p->set_text_color = as_ctrl_label_set_text_color;
    label_p->set_bg_img = as_ctrl_label_set_bg_img;
    label_p->get_text_color = as_ctrl_label_get_text_color;
    label_p->set_text_offset = as_ctrl_label_set_text_offset;
    label_p->set_bg_frame_color = as_ctrl_label_set_bg_frame_color;
    label_p->set_layout = as_ctrl_label_set_layout;
    label_p->set_bg_hidden = as_ctrl_label_set_bg_hidden;
    label_p->get_total_height = as_ctrl_lable_get_total_height;
    label_p->set_if_small = as_ctrl_label_set_if_small;
    label_p->text_color = 0x0;
    label_p->bg_color = AS_COLOR_WHITE;
    label_p->bg_frame_color = AS_COLOR_WHITE;
    label_p->layout = AS_LABEL_TEXT_CENTER;

    if(as_label_get_wstr_width(label_p) > area.w)
    {
        label_p->if_multiline = TRUE;
    }
    else
    {
        label_p->if_multiline = FALSE;
    }

    as_ctrl_label_calc_offset(label_p);
    label_p->total_height = as_ctrl_lable_get_total_height_internal((as_control*)label_p);
#ifndef MAX_LINE
    as_ctrl_label_analyze_text(label_p);
#endif
    return label_p;
}

as_ret as_ctrl_label_destroy(as_label* ctrl_label_p)
{
    if(!ctrl_label_p)
    {
        return AS_INVALID_PARAM;
    }

    if(ctrl_label_p->text)
    {
        as_free(ctrl_label_p->text);
        ctrl_label_p->text = NULL;
    }

    if(ctrl_label_p->bg_img)
    {
        as_ctrl_destroy((as_control*)ctrl_label_p->bg_img);
        ctrl_label_p->bg_img = NULL;
    }

    if(ctrl_label_p->text_pos_info)
    {
        as_free(ctrl_label_p->text_pos_info);
        ctrl_label_p->text_pos_info = ASNULL;
    }

    as_free(ctrl_label_p);
    ctrl_label_p = ASNULL;
    return AS_OK;
}